package io.youngledo.usb.license;

import lombok.extern.slf4j.Slf4j;

import java.security.*;
import java.util.Base64;

/// 许可证签名和验证工具
///
/// 提供RSA签名和验证功能，确保许可证的真实性和完整性。
///
/// ## 工作原理
/// 1. **签名**（开发者端，使用私钥）:
///    - 将许可证数据转换为字符串
///    - 使用私钥对数据进行SHA256withRSA签名
///    - 签名结果Base64编码后存入许可证
///
/// 2. **验证**（用户端，使用公钥）:
///    - 从许可证中提取签名
///    - 使用公钥验证签名是否匹配
///    - 如果签名验证通过，说明许可证未被篡改
///
/// ## 安全性
/// - 私钥仅开发者持有，用户无法获取
/// - 即使用户看到验证代码，也无法伪造签名
/// - 任何对许可证数据的修改都会导致签名验证失败
@Slf4j
public class LicenseValidator {

    private static final String SIGNATURE_ALGORITHM = "SHA256withRSA";

    /// **公钥字符串（硬编码在应用程序中）**
    ///
    /// ⚠️ **重要**: 这个公钥需要在首次运行 KeyManager.main() 后替换！
    ///
    /// 步骤：
    /// 1. 运行 `KeyManager.main()` 生成密钥对
    /// 2. 将 `public_key.pem` 的内容复制到这里
    /// 3. 保管好 `private_key.pem`（不要提交到Git）
    ///
    /// 当前公钥已从 KeyManager 生成。
    private static final String PUBLIC_KEY = """
            -----BEGIN PUBLIC KEY-----
            MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6A+5D6phuEZaEj1VCZ4n
            8YWUPb39CbEbHhH4QEEuS32l1VZ9nu65gR6sPKMwJ8xfUekaBPpYsKH71Q+H/VNF
            WjIz6x5G/b7QMn086Us383qGCYuzKtS+68eOdIhtHCncy3Y87tnRmnU4Mlc4s6oI
            LTERKSkHlMzaKqSHp5HXfO1DrY8hoGySR/XtSdv4wMgqpg76/O8qvGhnrjazPxjU
            uk9+0FBgAYmuBmzwkKxxRJkbVZzEtAuZ7Pgdh3ihQMoVLG5lSLvQN5VysDgcJSmy
            EPIrndh25U6We18eLwlbU3sgcEMrm0rZqp/0Vv46Hxp6ehrMKHSeEiPccMEOpxeF
            VwIDAQAB
            -----END PUBLIC KEY-----
            """;

    /// 使用私钥对许可证进行签名
    ///
    /// **此方法仅在开发者端使用**，用于生成许可证文件。
    ///
    /// @param license 要签名的许可证对象
    /// @param privateKey 私钥（从 private_key.pem 加载）
    /// @return Base64编码的签名字符串
    /// @throws Exception 如果签名失败
    public static String sign(License license, PrivateKey privateKey) throws Exception {
        var signature = Signature.getInstance(SIGNATURE_ALGORITHM);
        signature.initSign(privateKey);
        signature.update(license.getDataForSignature().getBytes());
        var signedBytes = signature.sign();
        return Base64.getEncoder().encodeToString(signedBytes);
    }

    /// 验证许可证签名
    ///
    /// **此方法在用户端使用**，验证许可证的真实性。
    /// 使用内置的公钥验证许可证签名是否有效。
    ///
    /// @param license 要验证的许可证对象
    /// @return 如果签名有效返回true，否则返回false
    public static boolean verify(License license) {
        try {
            // 社区版许可证无需验证签名
            if (license.type() == LicenseType.COMMUNITY) {
                return true;
            }

            // 加载内置公钥
            var publicKey = KeyManager.loadPublicKeyFromString(PUBLIC_KEY);

            // 验证签名
            var signature = Signature.getInstance(SIGNATURE_ALGORITHM);
            signature.initVerify(publicKey);
            signature.update(license.getDataForSignature().getBytes());

            var signatureBytes = Base64.getDecoder().decode(license.signature());
            boolean isValid = signature.verify(signatureBytes);

            if (isValid) {
                log.info("✅ 许可证签名验证成功: {}", license.licenseKey());
            } else {
                log.warn("❌ 许可证签名验证失败: {}", license.licenseKey());
            }

            return isValid;

        } catch (Exception e) {
            log.error("许可证验证过程中发生错误", e);
            return false;
        }
    }

    /// 验证许可证的完整性和有效性
    ///
    /// 执行全面的许可证检查：
    /// 1. 验证RSA签名
    /// 2. 检查是否过期
    /// 3. 可选：检查机器ID绑定
    ///
    /// @param license 要验证的许可证
    /// @param checkMachineId 是否检查机器ID绑定
    /// @return 如果许可证完全有效返回true
    public static boolean validateLicense(License license, boolean checkMachineId) {
        // 1. 验证签名
        if (!verify(license)) {
            log.warn("许可证签名无效");
            return false;
        }

        // 2. 检查是否过期
        if (license.isExpired()) {
            log.warn("许可证已过期: {}", license.expiryDate());
            return false;
        }

        // 3. 检查机器ID（可选）
        if (checkMachineId && license.machineId() != null) {
            var currentMachineId = MachineIdGenerator.getMachineId();
            if (!currentMachineId.equals(license.machineId())) {
                log.warn("许可证与当前机器不匹配");
                return false;
            }
        }

        log.info("✅ 许可证验证通过: {} ({})", license.userName(), license.type().getDisplayName());
        return true;
    }

    /// 简化的验证方法（不检查机器ID）
    ///
    /// @param license 要验证的许可证
    /// @return 如果许可证有效返回true
    public static boolean validateLicense(License license) {
        return validateLicense(license, false);
    }
}
